home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MacHack 1997
/
MacHack 1997.toast
/
Hacks
/
Hacks ’96
/
FinderFlocks
/
CBoid.cp
next >
Wrap
Text File
|
1996-06-23
|
9KB
|
341 lines
#include "CBoid.h"
#include "FlockDrawing.h"
#include "FinderRegistry.h"
extern long gWindowView;
//!•
GetIconBits(short id, Handle *theBits, Handle *theMaskBits);
CBoid* CBoid::InitBoid( Rect *worldRect, Handle theBits, Handle theMaskBits, Point position)
{
short wid, hgt, err;
Rect rect;
// Set other Particle variables
wid = worldRect->right - worldRect->left;
hgt = worldRect->bottom - worldRect->top;
this->fPosition.h = position.h;
this->fPosition.v = position.v;
this->fVelocity.h = 0;
this->fVelocity.v = 0;
this->fStartPoint = position;
this->fHome = false;
this->Reset(); // initializes the Neighbor record to none and sets acceleration to zero
err = this->InitBits(theBits, theMaskBits);
if(err) SysBeep(10);
return(this);
}
void CBoid::GetBoidPos(FloatPoint *pos)
{
*pos = this->fPosition;
}
void CBoid::GetBoidVel(FloatPoint *vel)
{
*vel = this->fVelocity;
}
// Re-initializes the Neighbor record and sets acceleration to zero
void CBoid::Reset(void)
{
// The neighbors
fNaybs.fNum = 0;
fNaybs.fAvgPos.h = 0;
fNaybs.fAvgPos.v = 0;
fNaybs.fAvgVel.h = 0;
fNaybs.fAvgVel.v = 0;
fNaybs.fAvgDistSquared = 0;
fNaybs.fNearestPos.h = 0;
fNaybs.fNearestPos.v = 0;
fNaybs.fNearestDistSquared = 2000000; // For later "are we closer than the last?" comparisons.
// We want the answer to the first one to be yes.
// The acceleration
fAcceleration.h = 0;
fAcceleration.v = 0;
}
// This routine does the flock thing to get the new position,
// wraps the position in the given Rect (toroidal world, don't ya know), and returns
// the new position in "newPos".
void CBoid::Move(FloatPoint *newPos, Rect *flockRect, ControlRec *controls, double comfydistsq)
{
double wid, hgt;
Rect rect;
short rposX, rposY;
GrafPtr currentPort;
// If we're not home, do the usual
if(!this->fHome)
{
// Look at neighbors and calculate new acceleration.
// This is where the flocking really happens.
NewAccel(comfydistsq, controls);
wid = flockRect->right - flockRect->left;
hgt = flockRect->bottom - flockRect->top;
// Keep the velocity within limits
Clamp(&this->fVelocity, controls->fMaxVelocityCtl);
// Step the boid
this->NextStep();
// Wrap the boid
if(this->fPosition.h < flockRect->left)
this->fPosition.h += wid;
else if(this->fPosition.h > flockRect->right)
this->fPosition.h -= wid;
// Same for v
if(this->fPosition.v < flockRect->top)
this->fPosition.v += hgt;
else if(this->fPosition.v > flockRect->bottom)
this->fPosition.v -= hgt;
}
else // we're near home, settle in
{
this->fPosition.h = this->fStartPoint.h;
this->fPosition.v = this->fStartPoint.v;
}
// return new position
*newPos = this->fPosition;
// convert new position to a rect
rposX = rint(this->fPosition.h);
rposY = rint(this->fPosition.v);
rect.left = rposX;
rect.top = rposY;
if(gWindowView == pIconBitmap)
{
rect.right = rposX + 32;
rect.bottom = rposY + 32;
}
else if(gWindowView == pSmallIcon)
{
rect.right = rposX + 16;
rect.bottom = rposY + 16;
}
// Draw!!
HLock(this->fTheMap);
// Let mask region catch up
OffsetRgn(this->fTheMaskRgn, rposX, rposY);
// OK, slam those bits
GetPort(¤tPort);
CopyBits((BitMap *)*this->fTheMap, ¤tPort->portBits,
&(**(BitMapHandle)this->fTheMap).bounds, &rect, srcCopy, this->fTheMaskRgn);
HUnlock(this->fTheMap);
// reset rgn for next time
OffsetRgn(this->fTheMaskRgn, -rposX, -rposY);
// Inval the rect
MarkRect(&rect);
// Reset for next time
Reset();
}
// This routine gets acceleration requests from each of the three "rules" in order,
// and arbitrates the results: Each request is added into an Acceleration accumulator
// (AccAcc), and it's magnitude is added into another, scalar accumulator (MagAcc).
// When the accumulated magnitudes equal or exceed the maximum allowable "oompf,"
// further requests are denied (actually, not even requested). The final accumulated
// acceleration is trimmed back to a reasonable value and then set as the new
// acceleration.
void CBoid::NewAccel(double comfydistsq, ControlRec *controls)
{
long leftover = controls->fMaxEffortCtl;
FloatPoint AccAcc = {0, 0}, request = {0, 0};
// If there are no neighbors, do nothing
if(this->fNaybs.fNum == 0)
return;
// First, avoid collisions
if(leftover > 0)
{
RunAway(comfydistsq, &request);
Clamp(&request, controls->fAvoidMaxCtl);
AccAcc = request;
// Subtract the "energy" used by this request
leftover -= VecMagSq(request.h, request.v);
// if something leftover, do next rule
if(leftover > 0)
{
// Match the neighbors average velocity
MatchVel(&request);
Clamp(&request, controls->fMatchMaxCtl);
AccAcc.h += request.h;
AccAcc.v += request.v;
// Subtract the "energy" used by this request
leftover -= VecMagSq(request.h, request.v);
// if something leftover, do next rule
if(leftover > 0)
{
// Try to move toward the "center" of the neighbors
CatchUp(&request);
Clamp(&request, controls->fCenterMaxCtl);
AccAcc.h += request.h;
AccAcc.v += request.v;
}
}
// Trim new acceleration
Clamp(&AccAcc, controls->fMaxAccelCtl);
}
this->fAcceleration = AccAcc;
}
void CBoid :: RunAway(double comfydistsq, FloatPoint *request)
{
double scaler;
double distsq;
double biggest;
// First get the distance squared to the nearest neighbor
distsq = (fNaybs.fNearestDistSquared > 0) ? fNaybs.fNearestDistSquared : 0.01; // To avoid dividing by 0
// if distsq > comfydistsq, request no change
if(distsq > comfydistsq)
{
request->h = 0;
request->v = 0;
}
else // Check it out
{
// get the vector pointing from the nearest neighbor to us
request->h = fPosition.h - fNaybs.fNearestPos.h;
request->v = fPosition.v - fNaybs.fNearestPos.v;
biggest = (fabs(request->v) > fabs(request->h)) ? fabs(request->v) : fabs(request->h);
if(biggest == 0)
{
request->h = Random() % 4;
request->v = Random() % 4;
biggest = (fabs(request->v) > fabs(request->h)) ? fabs(request->v) : fabs(request->h);
if(biggest == 0) biggest = 1;
}
scaler = comfydistsq / biggest;
request->h = (request->h * scaler) / distsq;
request->v = (request->v * scaler) / distsq;
}
}
void CBoid :: MatchVel(FloatPoint *request)
{
// Just return the difference from the neighbors' average velocity
request->h = (fNaybs.fAvgVel.h - fVelocity.h);
request->v = (fNaybs.fAvgVel.v - fVelocity.v);
}
void CBoid :: CatchUp(FloatPoint *request)
{
// Just return the vector pointing toward the center of the neighbors
request->h = (fNaybs.fAvgPos.h - fPosition.h);
request->v = (fNaybs.fAvgPos.v - fPosition.v);
}
//----------------------------------------------
// •• Icon junk
//----------------------------------------------
OSErr CBoid :: InitBits(Handle theBits, Handle theMaskBits)
{
OSErr err;
Rect mapRect;
long bitsSize, maskOffset;
Handle theMaskBitmap;
if(gWindowView == pIconBitmap)
{
SetRect(&mapRect, 0, 0, 32, 32);
maskOffset = 128;
}
else if(gWindowView == pSmallIcon)
{
SetRect(&mapRect, 0, 0, 16, 16);
maskOffset = 32;
}
else
return -1;
if(theBits != nil)
{
bitsSize = BuildMap(&fTheMap, &mapRect, 8);
if(bitsSize == 0) return memFullErr;
fTheBits = NewPtr(bitsSize);
if(fTheBits == nil) return memFullErr;
HLock(theBits);
BlockMove(*theBits, fTheBits, bitsSize);
HUnlock(theBits);
}
else // if theBits is nil, no 8 bit icon, use the black and white icon
{
bitsSize = BuildMap(&fTheMap, &mapRect, 1);
if(bitsSize == 0) return memFullErr;
fTheBits = NewPtr(bitsSize);
if(fTheBits == nil) return memFullErr;
HLock(theMaskBits);
BlockMove(*theMaskBits, fTheBits, bitsSize);
HUnlock(theMaskBits);
}
((BitMap *)(*fTheMap))->baseAddr = fTheBits;
// Set up mask Rgn
bitsSize = BuildMap(&theMaskBitmap, &mapRect, 1);
if(bitsSize == 0) return memFullErr;
HLock(theMaskBits);
HLock(theMaskBitmap);
((BitMap *)(*theMaskBitmap))->baseAddr = (*theMaskBits) + maskOffset;
fTheMaskRgn = NewRgn();
BitMapToRegion(fTheMaskRgn, (BitMap *)(*theMaskBitmap));
HUnlock(theMaskBits);
HUnlock(theMaskBitmap);
DisposeHandle(theMaskBitmap);
return noErr;
}
void CBoid :: Nudge(void)
{
this->fPosition.h += Random() % 3;
this->fPosition.v += Random() % 3;
}
Boolean CBoid :: HeadHome(void)
{
// Still getting there
this->fNaybs.fNum = 1;
this->fNaybs.fAvgPos.h = this->fStartPoint.h;
this->fNaybs.fAvgPos.v = this->fStartPoint.v;
this->fNaybs.fAvgVel.h = 0;
this->fNaybs.fAvgVel.v = 0;
this->fNaybs.fAvgDistSquared =
VecMagSq(this->fPosition.h - this->fNaybs.fAvgPos.h,
this->fPosition.v - this->fNaybs.fAvgPos.v );
// if we're near home, stop
if(this->fNaybs.fAvgDistSquared <= 25)
this->fHome = true;
return this->fHome;
}